home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1993 July / InfoMagic USENET CD-ROM July 1993.ISO / sources / misc / volume25 / input-edit / part01 next >
Encoding:
Text File  |  1991-11-12  |  22.7 KB  |  758 lines

  1. Newsgroups: comp.sources.misc
  2. From: thewalt@canuck.CE.Berkeley.EDU (Chris Thewalt)
  3. Subject:  v25i056:  input-edit - C input functions for line editing with history, Part01/01
  4. Message-ID: <1991Nov13.033151.17617@sparky.imd.sterling.com>
  5. X-Md4-Signature: 9dbf7f3e9d651adf33032942de7869c2
  6. Date: Wed, 13 Nov 1991 03:31:51 GMT
  7. Approved: kent@sparky.imd.sterling.com
  8.  
  9. Submitted-by: thewalt@canuck.CE.Berkeley.EDU (Chris Thewalt)
  10. Posting-number: Volume 25, Issue 56
  11. Archive-name: input-edit/part01
  12. Environment: UNIX, MS-DOS
  13.  
  14. environment:  ANSI C, UNIX (sysv or bsd) or MSDOS with MSC
  15.               (Converting to K&R C should be fairly simple)
  16.          
  17. tested on:    DECstation 5000, Ultrix 4.2 with gcc
  18.           Sun Sparc II, SunOS 4.1.1, gcc
  19.           SGI Iris, IRIX System V.3, cc
  20.           PC, DR DOS 5.0, MSC 6.0
  21.  
  22. description: The input-edit package can be used in programs that
  23.              want to provide an emacs style line editing cabability
  24.          with history. The function getline allows the user to
  25.          edit the current line and move through the history list
  26.          of lines previously typed and returns the buffer to the
  27.          caller when RETURN is entered.  Long lines are handled
  28.          by horizontal scrolling. Does NOT use termcap, uses
  29.          only \b to move around. 
  30.  
  31.              The actual editing capabilites are a very small subset
  32.          of emacs commands, but then the package is very small
  33.          and  quite portable.  Get GNU readline if you need more
  34.          powerful editing.
  35.  
  36. Chris (thewalt@ce.berkeley.edu)
  37. ---------------------------------------------------------------------
  38. #!/bin/sh
  39. # This is a shell archive (produced by shar 3.49)
  40. # To extract the files from this archive, save it to a file, remove
  41. # everything above the "!/bin/sh" line above, and type "sh file_name".
  42. #
  43. # made 11/09/1991 20:30 UTC by thewalt@canuck.CE.Berkeley.EDU
  44. # Source directory /usr/users/thewalt/et/gl
  45. #
  46. # existing files will NOT be overwritten unless -c is specified
  47. #
  48. # This shar contains:
  49. # length  mode       name
  50. # ------ ---------- ------------------------------------------
  51. #   1276 -rw-r--r-- README
  52. #     98 -rw-r--r-- Makefile
  53. #  17056 -rw-r--r-- getline.c
  54. #    293 -rw-r--r-- testgl.c
  55. #
  56. # ============= README ==============
  57. if test -f 'README' -a X"$1" != X"-c"; then
  58.     echo 'x - skipping README (File already exists)'
  59. else
  60. echo 'x - extracting README (Text)'
  61. sed 's/^X//' << 'SHAR_EOF' > 'README' &&
  62. Send bug reports, fixes and enhancements to:
  63. Chris Thewalt (thewalt@ce.berkeley.edu)   
  64. 11/9/91
  65. X
  66. A simple driver called testgl.c has been included, all it does is echo each
  67. line of input that is read.  The user can perform editing and history 
  68. manipulation before hitting RET.
  69. X
  70. The details of getline are all included as comments within getline.c
  71. X
  72. environment:  ANSI C, UNIX (sysv or bsd) or MSDOS with MSC
  73. X              (Converting to K&R C should be fairly simple)
  74. X         
  75. tested on:    DECstation 5000, Ultrix 4.2 with gcc
  76. X          Sun Sparc II, SunOS 4.1.1, gcc
  77. X          SGI Iris, IRIX System V.3, cc
  78. X          PC, DR DOS 5.0, MSC 6.0
  79. X
  80. description: Getline can be used in programs that want to provide
  81. X             an emacs style line editing cabability with history.
  82. X         Getline allows the user to edit the current line
  83. X             and move through the history list of lines previously
  84. X         typed and returns the buffer to the caller when RETURN
  85. X         is entered.  Long lines are handled by scrolling.
  86. X         Does NOT use termcap, uses only \b to move around. 
  87. X
  88. X             The actual editing capabilites are a very small subset
  89. X         of emacs commands, but then the package is very small
  90. X         and  quite portable.  Get GNU readline if you need more
  91. X         powerful editing.
  92. SHAR_EOF
  93. chmod 0644 README ||
  94. echo 'restore of README failed'
  95. Wc_c="`wc -c < 'README'`"
  96. test 1276 -eq "$Wc_c" ||
  97.     echo 'README: original size 1276, current size' "$Wc_c"
  98. fi
  99. # ============= Makefile ==============
  100. if test -f 'Makefile' -a X"$1" != X"-c"; then
  101.     echo 'x - skipping Makefile (File already exists)'
  102. else
  103. echo 'x - extracting Makefile (Text)'
  104. sed 's/^X//' << 'SHAR_EOF' > 'Makefile' &&
  105. OBJS = testgl.o getline.o
  106. X
  107. testgl: $(OBJS)
  108. X    $(CC) -o testgl $(OBJS)
  109. X
  110. clean:
  111. X    rm -f $(OBJS) testgl
  112. SHAR_EOF
  113. chmod 0644 Makefile ||
  114. echo 'restore of Makefile failed'
  115. Wc_c="`wc -c < 'Makefile'`"
  116. test 98 -eq "$Wc_c" ||
  117.     echo 'Makefile: original size 98, current size' "$Wc_c"
  118. fi
  119. # ============= getline.c ==============
  120. if test -f 'getline.c' -a X"$1" != X"-c"; then
  121.     echo 'x - skipping getline.c (File already exists)'
  122. else
  123. echo 'x - extracting getline.c (Text)'
  124. sed 's/^X//' << 'SHAR_EOF' > 'getline.c' &&
  125. #ifndef lint
  126. static char     rcsid[] =
  127. "$Id: getline.c,v 1.8 1991/11/09 20:27:48 thewalt Exp thewalt $";
  128. #endif
  129. X
  130. /* 
  131. X * Fairly portable (ANSI C),  emacs style line editing input package.  
  132. X * This package uses \b to move, and \007 to ring the bell.  
  133. X * It uses a fixed screen width, as initialized in the gl_init() call,
  134. X * and does not draw in the last location to avoid line wraps.
  135. X * The only non-portable part is how to turn off character echoing.
  136. X * This code works for *NIX of BSD or SYSV flavor, as well as MSDOS (MSC6.0).
  137. X * No TERMCAP features are used, so long lines are scrolled on one line 
  138. X * rather than extending over several lines.  The function getline 
  139. X * returns a pointer to a static buffer area which holds the input string, 
  140. X * including the newline. On EOF the first character is set equal to '\0'.  
  141. X * The caller supplies a desired prompt, as shown in the prototype:
  142. X *
  143. X *      char *getline(char *prompt)
  144. X *
  145. X * Getline is different from GNU readline in that:
  146. X *    - getline has much more limited editing capabilities, but it
  147. X *      is also much smaller and doesn't need termcap.
  148. X *    - you don't free the buffer when done, since it is static 
  149. X *      (make a copy yourself if you want one)
  150. X *    - the newline is appended to the buffer
  151. X *    - you don't add lines to history, it is done automatically.
  152. X *
  153. X * The function gl_init(int screen_width) should be called before 
  154. X * calling getline(char *prompt), and gl_cleanup(void) should be 
  155. X * called before before exiting.  The function gl_redraw(void) may also 
  156. X * be called to redraw the screen (as is done when ^L or ^R are read).
  157. X *
  158. X * The editing keys are:
  159. X *  ^A, ^E   - move to beginnning or end of line, respectively.
  160. X *  ^F, ^B   - nondestructive move forward or back one location, respectively.
  161. X *  ^D       - delete the character currently under the cursor, or
  162. X *             send EOF if no characters in the buffer.
  163. X *  ^H, DEL  - delete character left of the cursor.
  164. X *  ^K       - delete from cursor to end of line.
  165. X *  ^P, ^N   - move through history, previous and next, respectively.
  166. X *  ^L, ^R   - redraw the current line.
  167. X *  TAB      - call user defined function if bound, or insert spaces
  168. X *             to get to next TAB stop (just past every 8th column).
  169. X *  NL, CR   - places line on history list if nonblank, calls output
  170. X *             cleanup function if bound, appends newline and returns
  171. X *             to the caller.
  172. X *
  173. X * In addition, the caller can modify the buffer in certain ways, which
  174. X * may be useful for things like auto-indent modes.  There are three
  175. X * function pointers which can be bound to user functions.
  176. X * Each of these functions must return the index of the first location 
  177. X * at which the buffer was modified, or -1 if the buffer wasn't modified.  
  178. X * Each of the functions receive the current input buffer as the first 
  179. X * argument.  The screen is automatically cleaned up if the buffer is changed.
  180. X * The user is responsible for not writing beyond the end of the static 
  181. X * buffer.  The function pointer prototypes are:
  182. X * 
  183. X *   int (*gl_in_hook)(char *buf)
  184. X *       - called on entry to getline, and each time a new history
  185. X *         string is loaded, from a ^P or ^N. Initally NULL.
  186. X *   int (*gl_out_hook)(char *buf)
  187. X *       - called when a \n or \r is read, before appending \n and
  188. X *         returning to caller. Initally NULL.
  189. X *   int (*gl_tab_hook)(char *buf, int offset, int *loc)
  190. X *       - called whenever a TAB is read.  The second argument is
  191. X *         the current line offset due to the width of the prompt. 
  192. X *         The third argument is a pointer to the variable holding the 
  193. X *         current location in the buffer.  The location may be reset 
  194. X *         by the user to move the cursor when the call returns.
  195. X *         Initially a built in tabbing function is bound.
  196. X *
  197. X * Please send bug reports, fixes and enhancements to Chris Thewalt,
  198. X * thewalt@ce.berkeley.edu
  199. X */
  200. X
  201. static char *copyright = "Copyright (C) 1991, Chris Thewalt";
  202. /*
  203. X * Copyright (C) 1991 by Chris Thewalt
  204. X *
  205. X * Permission to use, copy, modify, and distribute this software 
  206. X * for any purpose and without fee is hereby granted, provided
  207. X * that the above copyright notices appear in all copies and that both the
  208. X * copyright notice and this permission notice appear in supporting
  209. X * documentation.  This software is provided "as is" without express or
  210. X * implied warranty.
  211. X */
  212. X
  213. #include <stdio.h>
  214. #include <stdlib.h>
  215. #include <string.h>
  216. #include <ctype.h>
  217. X
  218. extern int      isatty();    
  219. X
  220. #define BUF_SIZE 1024
  221. #define SCROLL   30
  222. X
  223. static int      gl_init_done = 0;    /* -1 is terminal, 1 is batch  */
  224. static int      gl_screen = 80;        /* screen width */
  225. static int      gl_width = 0;        /* net size available for input */
  226. static int      gl_pos, gl_cnt = 0;     /* position and size of input */
  227. static char     gl_buf[BUF_SIZE];       /* input buffer */
  228. static char    *gl_prompt;        /* to save the prompt string */
  229. X
  230. void            gl_init(int);        
  231. void            gl_cleanup(void);    /* to undo gl_init */
  232. void            gl_redraw(void);    /* issue \n and redraw all */
  233. static void     gl_char_init(void);    /* get ready for no echo input */
  234. static void     gl_char_cleanup(void);    /* undo gl_char_init */
  235. static int      gl_getchar(void);       /* read one char from stdin */
  236. static void     gl_addchar(int);    /* install specified char */
  237. static void     gl_newline(void);    /* handle \n or \r */
  238. static void     gl_fixup(int, int);    /* fixup state variables and screen */
  239. static void     gl_del(int);        /* del, either left (-1) or cur (0) */
  240. static void     gl_kill(void);        /* delete to EOL */
  241. static int      gl_tab(char *, int, int *);    /* default TAB handler */
  242. X
  243. static void     hist_add(void);        /* adds nonblank entries to hist */
  244. static void     hist_init(void);    /* initializes hist pointers */
  245. static void     hist_next(void);    /* copies next entry to input buf */
  246. static void     hist_prev(void);    /* copies prev entry to input buf */
  247. static char    *hist_save(char *);    /* makes copy of a string */
  248. X
  249. int         (*gl_in_hook)(char *) = 0;
  250. int         (*gl_out_hook)(char *) = 0;
  251. int         (*gl_tab_hook)(char *, int, int *) = gl_tab;
  252. X
  253. /************************ nonportable part *********************************/
  254. #ifdef MSDOS
  255. #include <bios.h>
  256. #endif
  257. X
  258. #ifdef unix
  259. #include <sys/ioctl.h>
  260. #ifndef TIOCGETP
  261. #include <termio.h>
  262. struct termio tty, old_tty;
  263. #else
  264. #include <sgtty.h>
  265. struct sgttyb   tty, old_tty;
  266. #endif /* TIOCGETP */
  267. extern int      ioctl();
  268. #endif    /* unix */
  269. X
  270. static void
  271. gl_char_init()
  272. /* turn off input echo */
  273. {
  274. #ifdef unix
  275. #ifdef TIOCGETP
  276. X    ioctl(0, TIOCGETP, &old_tty);
  277. X    tty = old_tty;
  278. X    tty.sg_flags |= CBREAK;
  279. X    tty.sg_flags &= ~ECHO;
  280. X    ioctl(0, TIOCSETP, &tty);
  281. #else
  282. X    ioctl(0, TCGETA, &old_tty);
  283. X    tty = old_tty;
  284. X    tty.c_lflag &= ~(ICANON|ECHO|ECHOE|ECHOK|ECHONL);
  285. X    tty.c_cc[VMIN] = 1;
  286. X    ioctl(0, TCSETA, &tty);
  287. #endif 
  288. #endif /* unix */
  289. }
  290. X
  291. static void
  292. gl_char_cleanup()
  293. /* undo effects of gl_char_init, as necessary */
  294. {
  295. #ifdef unix
  296. #ifdef TIOCSETP
  297. X    ioctl(0, TIOCSETP, &old_tty);
  298. #else
  299. X    ioctl(0, TCSETA, &old_tty);
  300. #endif
  301. #endif /* unix */
  302. }
  303. X
  304. static int
  305. gl_getchar()
  306. /* get a character without echoing it to screen */
  307. {
  308. X    int             c;
  309. X
  310. #ifdef unix
  311. X    c = fgetc(stdin);
  312. #endif
  313. #ifdef MSDOS
  314. X    c = _bios_keybrd(_NKEYBRD_READ) & 0177;   /* only using 7 bit ASCII */
  315. #endif
  316. X    return c;
  317. }
  318. /******************** fairly portable part *********************************/
  319. void
  320. gl_init(int scrn_wdth)
  321. /* set up variables and terminal */
  322. {
  323. X    gl_screen = scrn_wdth;
  324. X    if (gl_init_done == 0) {
  325. X        hist_init();
  326. X        if (isatty(0) && isatty(1)) { 
  327. X            setvbuf(stdin, (char *)0, _IONBF, 0);
  328. X            setvbuf(stdout, (char *)0, _IONBF, 0);
  329. X        gl_char_init();
  330. X            gl_init_done = -1;        /* -1 means terminal */
  331. X        } else { 
  332. X            gl_init_done = 1;        /* 1 means batch */
  333. X    }
  334. X    }
  335. X    gl_pos = gl_cnt = 0;
  336. }
  337. X
  338. void
  339. gl_cleanup()
  340. /* undo effects of gl_init, as necessary */
  341. {
  342. X    if (gl_init_done == -1)
  343. X        gl_char_cleanup();
  344. X    gl_init_done = 0;
  345. }
  346. X
  347. char *
  348. getline(char *prompt)
  349. {
  350. X    int             c, loc, tmp;
  351. X
  352. X    if (!gl_init_done)
  353. X    gl_init(80);
  354. X    gl_buf[0] = 0;        /* used as end of input indicator */
  355. X    if (gl_init_done == 1) {    /* no input editing, and no prompt output */
  356. X    fgets(gl_buf, BUF_SIZE, stdin);
  357. X    return gl_buf;
  358. X    }
  359. X    gl_fixup(-1, 0);            /* this resets gl_fixup */
  360. X    gl_width = gl_screen - strlen(prompt);
  361. X    if (prompt == NULL)    
  362. X    prompt = "";
  363. X    gl_prompt = prompt;
  364. X    gl_pos = gl_cnt = 0;
  365. X    fputs(prompt, stdout);
  366. X    if (gl_in_hook) {
  367. X    loc = gl_in_hook(gl_buf);
  368. X    if (loc >= 0)
  369. X        gl_fixup(0, BUF_SIZE);
  370. X    }
  371. X    while ((c = gl_getchar()) != EOF) {
  372. X    if (isprint(c)) {
  373. X        gl_addchar(c);
  374. X    } else {
  375. X        switch (c) {
  376. X          case '\n': case '\r':             /* newline */
  377. X        gl_newline();
  378. X        return gl_buf;
  379. X        break;
  380. X          case '\001': gl_fixup(-1, 0);        /* ^A */
  381. X        break;
  382. X          case '\002': gl_fixup(-1, gl_pos-1);    /* ^B */
  383. X        break;
  384. X          case '\004':                /* ^D */
  385. X        if (gl_cnt == 0) {
  386. X            gl_buf[0] = 0;
  387. X            gl_cleanup();
  388. X            fputc('\n', stdout);
  389. X            return gl_buf;
  390. X        } else {
  391. X            gl_del(0);
  392. X        }
  393. X        break;
  394. X          case '\005': gl_fixup(-1, gl_cnt);    /* ^E */
  395. X        break;
  396. X          case '\006': gl_fixup(-1, gl_pos+1);    /* ^F */
  397. X        break;
  398. X          case '\010': case '\177': gl_del(-1);    /* ^H and DEL */
  399. X        break;
  400. X          case '\t':                    /* TAB */
  401. X                if (gl_tab_hook) {
  402. X            tmp = gl_pos;
  403. X                loc = gl_tab_hook(gl_buf, strlen(gl_prompt), &tmp);
  404. X                if (loc >= 0 || tmp != gl_pos)
  405. X                    gl_fixup(loc, tmp);
  406. X                }
  407. X        break;
  408. X          case '\013': gl_kill();            /* ^K */
  409. X        break;
  410. X          case '\014': case '\022':    gl_redraw();    /* ^L and ^R */
  411. X        break;
  412. X          case '\016': hist_next();            /* ^N */
  413. X        break;
  414. X          case '\020': hist_prev();            /* ^P */
  415. X        break;
  416. X          default:
  417. X        fputc('\007', stdout);
  418. X        break;
  419. X        }
  420. X    }
  421. X    }
  422. X    gl_cleanup();
  423. X    return gl_buf;
  424. }
  425. X
  426. static void
  427. gl_addchar(int c)
  428. /* adds the character c to the input buffer at current location */
  429. {
  430. X    int  i;
  431. X
  432. X    if (gl_cnt >= BUF_SIZE - 1) {
  433. X    fprintf(stderr, "getline: input buffer overflow\n");
  434. X    exit(1);
  435. X    }
  436. X    for (i=gl_cnt; i >= gl_pos; i--)
  437. X         gl_buf[i+1] = gl_buf[i];
  438. X    gl_buf[gl_pos] = c;
  439. X    gl_fixup(gl_pos, gl_pos+1);
  440. }
  441. X
  442. static void
  443. gl_newline()
  444. /*
  445. X * Cleans up entire line before returning to caller. A \n is appended.
  446. X * If line longer than screen, we redraw starting at beginning
  447. X */
  448. {
  449. X    int change = gl_cnt;
  450. X    int len = gl_cnt;
  451. X    int loc = gl_width - 5;    /* shifts line back to start position */
  452. X
  453. X    if (gl_cnt >= BUF_SIZE - 1) {
  454. X    fprintf(stderr, "getline: input buffer overflow\n");
  455. X    exit(1);
  456. X    }
  457. X    hist_add();            /* only adds if nonblank */
  458. X    if (gl_out_hook) {
  459. X    change = gl_out_hook(gl_buf);
  460. X        len = strlen(gl_buf);
  461. X    } 
  462. X    if (loc > len)
  463. X    loc = len;
  464. X    gl_fixup(change, loc);    /* must do this before appending \n */
  465. X    gl_buf[len] = '\n';
  466. X    gl_buf[len+1] = '\0';
  467. X    fputc('\n', stdout);
  468. }
  469. X
  470. static void
  471. gl_del(int loc)
  472. /*
  473. X * Delete a character.  The loc variable can be:
  474. X *    -1 : delete character to left of cursor
  475. X *     0 : delete character under cursor
  476. X */
  477. {
  478. X    int i;
  479. X
  480. X    if (loc == -1 && gl_pos > 0 || loc == 0 && gl_pos < gl_cnt) {
  481. X        for (i=gl_pos+loc; i < gl_cnt; i++)
  482. X        gl_buf[i] = gl_buf[i+1];
  483. X    gl_fixup(gl_pos+loc, gl_pos+loc);
  484. X    } else
  485. X    fputc('\007', stdout);
  486. }
  487. X
  488. static void
  489. gl_kill()
  490. /* delete from current position to the end of line */
  491. {
  492. X    if (gl_pos < gl_cnt) {
  493. X    gl_buf[gl_pos] = '\0';
  494. X    gl_fixup(gl_pos, gl_pos);
  495. X    } else
  496. X    fputc('\007', stdout);
  497. }
  498. X
  499. void
  500. gl_redraw()
  501. /* emit a newline, reset and redraw prompt and current input line */
  502. {
  503. X    if (gl_init_done == -1) {
  504. X        fputc('\n', stdout);
  505. X        fputs(gl_prompt, stdout);
  506. X        gl_pos = 0;
  507. X        gl_fixup(0, BUF_SIZE);
  508. X    }
  509. }
  510. X
  511. static void
  512. gl_fixup(int change, int cursor)
  513. /*
  514. X * This function is used both for redrawing when input changes or for
  515. X * moving within the input line.  The parameters are:
  516. X *   change : the index of the start of changes in the input buffer,
  517. X *            with -1 indicating no changes.
  518. X *   cursor : the desired location of the cursor after the call.
  519. X *            A value of BUF_SIZE can be used  to indicate the cursor should
  520. X *            move just past the end of the input line.
  521. X */
  522. {
  523. X    static int   gl_shift;    /* index of first on screen character */
  524. X    static int   off_right;    /* true if more text right of screen */
  525. X    static int   off_left;    /* true if more text left of screen */
  526. X    int          left = 0, right = -1;        /* bounds for redraw */
  527. X    int          pad;        /* how much to erase at end of line */
  528. X    int          backup;        /* how far to backup before fixing */
  529. X    int          new_shift;     /* value of shift based on cursor */
  530. X    int          extra;         /* adjusts when shift (scroll) happens */
  531. X    int          i;
  532. X
  533. X    if (change == -1 && cursor == 0 && gl_buf[0] == 0) {   /* reset */
  534. X    gl_shift = off_right = off_left = 0;
  535. X    return;
  536. X    }
  537. X    pad = (off_right)? gl_width - 1 : gl_cnt - gl_shift;   /* old length */
  538. X    backup = gl_pos - gl_shift;
  539. X    if (change >= 0) {
  540. X        gl_cnt = strlen(gl_buf);
  541. X        if (change > gl_cnt)
  542. X        change = gl_cnt;
  543. X    }
  544. X    if (cursor > gl_cnt) {
  545. X    if (cursor != BUF_SIZE)        /* BUF_SIZE means end of line */
  546. X        fputc('\007', stdout);
  547. X    cursor = gl_cnt;
  548. X    }
  549. X    if (cursor < 0) {
  550. X    fputc('\007', stdout);
  551. X    cursor = 0;
  552. X    }
  553. X    if (off_right || off_left && cursor < gl_shift + gl_width - SCROLL / 2)
  554. X    extra = 2;            /* shift the scrolling boundary */
  555. X    else 
  556. X    extra = 0;
  557. X    new_shift = cursor + extra + SCROLL - gl_width;
  558. X    if (new_shift > 0) {
  559. X    new_shift /= SCROLL;
  560. X    new_shift *= SCROLL;
  561. X    } else
  562. X    new_shift = 0;
  563. X    if (new_shift != gl_shift) {    /* scroll occurs */
  564. X    gl_shift = new_shift;
  565. X    off_left = (gl_shift)? 1 : 0;
  566. X    off_right = (gl_cnt > gl_shift + gl_width - 1)? 1 : 0;
  567. X        left = gl_shift;
  568. X    right = (off_right)? gl_shift + gl_width - 2 : gl_cnt;
  569. X    } else if (change >= 0) {        /* no scroll, but text changed */
  570. X    if (change < gl_shift + off_left) {
  571. X        left = gl_shift;
  572. X    } else {
  573. X        left = change;
  574. X        backup = gl_pos - change;
  575. X    }
  576. X    off_right = (gl_cnt > gl_shift + gl_width - 1)? 1 : 0;
  577. X    right = (off_right)? gl_shift + gl_width - 2 : gl_cnt;
  578. X    }
  579. X    pad -= (off_right)? gl_width - 1 : gl_cnt - gl_shift;
  580. X    pad = (pad < 0)? 0 : pad;
  581. X    if (left <= right) {        /* clean up screen */
  582. X    for (i=0; i < backup; i++)
  583. X        fputc('\b', stdout);
  584. X    if (left == gl_shift && off_left) {
  585. X        fputc('$', stdout);
  586. X        left++;
  587. X        }
  588. X    for (i=left; i < right; i++)
  589. X        fputc(gl_buf[i], stdout);
  590. X    if (off_right) {
  591. X        fputc('$', stdout);
  592. X        gl_pos = right + 1;
  593. X    } else { 
  594. X        for (i=0; i < pad; i++)    /* erase remains of prev line */
  595. X        fputc(' ', stdout);
  596. X        gl_pos = right + pad;
  597. X    }
  598. X    }
  599. X    i = gl_pos - cursor;        /* move to final cursor location */
  600. X    if (i > 0) {
  601. X    while (i--)
  602. X       fputc('\b', stdout);
  603. X    } else {
  604. X    for (i=gl_pos; i < cursor; i++)
  605. X        fputc(gl_buf[i], stdout);
  606. X    }
  607. X    gl_pos = cursor;
  608. }
  609. X
  610. static int
  611. gl_tab(char *buf, int offset, int *loc)
  612. /* default tab handler, acts like tabstops every 8 cols */
  613. {
  614. X    int i, count, len;
  615. X
  616. X    len = strlen(buf);
  617. X    count = 8 - (offset + *loc) % 8;
  618. X    for (i=len; i >= *loc; i--)
  619. X        buf[i+count] = buf[i];
  620. X    for (i=0; i < count; i++)
  621. X        buf[*loc+i] = ' ';
  622. X    i = *loc;
  623. X    *loc = i + count;
  624. X    return i;
  625. }
  626. X
  627. /******************* History stuff **************************************/
  628. X
  629. #ifndef HIST_SIZE
  630. #define HIST_SIZE 100
  631. #endif
  632. X
  633. int             hist_pos, hist_last;
  634. char           *hist_buf[HIST_SIZE];
  635. X
  636. static void
  637. hist_init()
  638. {
  639. X    int i;
  640. X
  641. X    for (i=0; i < HIST_SIZE; i++)
  642. X    hist_buf[i] = (char *)NULL;
  643. }
  644. X
  645. static void
  646. hist_add()
  647. {
  648. X    char *p = gl_buf;
  649. X
  650. X    while (*p == ' ' || *p == '\t')    /* only save nonblank line */
  651. X    p++;
  652. X    if (*p) {
  653. X        hist_buf[hist_last] = hist_save(gl_buf);
  654. X        hist_last = (hist_last + 1) % HIST_SIZE;
  655. X        if (hist_buf[hist_last]) {    /* erase next location */
  656. X        free(hist_buf[hist_last]);
  657. X        hist_buf[hist_last] = NULL;
  658. X        }
  659. X    }
  660. X    hist_pos = hist_last;
  661. }
  662. X
  663. static void
  664. hist_prev()
  665. /* loads previous hist entry into input buffer, sticks on first */
  666. {
  667. X    int   next;
  668. X
  669. X    next = (hist_pos - 1 + HIST_SIZE) % HIST_SIZE;
  670. X    if (next != hist_last) {
  671. X        if (hist_buf[next]) {
  672. X        hist_pos = next;
  673. X        strcpy(gl_buf, hist_buf[hist_pos]);
  674. X    } else
  675. X        fputc('\007', stdout);
  676. X    } else
  677. X    fputc('\007', stdout);
  678. X    if (gl_in_hook)
  679. X    gl_in_hook(gl_buf);
  680. X    gl_fixup(0, BUF_SIZE);
  681. }
  682. X
  683. static void
  684. hist_next()
  685. /* loads next hist entry into input buffer, clears on last */
  686. {
  687. X    if (hist_pos != hist_last) {
  688. X    hist_pos = (hist_pos + 1) % HIST_SIZE;
  689. X    if (hist_buf[hist_pos]) {
  690. X        strcpy(gl_buf, hist_buf[hist_pos]);
  691. X    } else {
  692. X        gl_buf[0] = 0;
  693. X    }
  694. X    } else
  695. X    fputc('\007', stdout);
  696. X    if (gl_in_hook) 
  697. X    gl_in_hook(gl_buf);
  698. X    gl_fixup(0, BUF_SIZE);
  699. }
  700. X
  701. static char *
  702. hist_save(char *p)
  703. /* makes a copy of the string */
  704. {
  705. X    char *s = NULL;
  706. X
  707. X    if (p && ((s = malloc(strlen(p)+1)) != NULL)) {
  708. X            strcpy(s, p);
  709. X    }
  710. X    return s;
  711. }
  712. SHAR_EOF
  713. chmod 0644 getline.c ||
  714. echo 'restore of getline.c failed'
  715. Wc_c="`wc -c < 'getline.c'`"
  716. test 17056 -eq "$Wc_c" ||
  717.     echo 'getline.c: original size 17056, current size' "$Wc_c"
  718. fi
  719. # ============= testgl.c ==============
  720. if test -f 'testgl.c' -a X"$1" != X"-c"; then
  721.     echo 'x - skipping testgl.c (File already exists)'
  722. else
  723. echo 'x - extracting testgl.c (Text)'
  724. sed 's/^X//' << 'SHAR_EOF' > 'testgl.c' &&
  725. #include <stdio.h>
  726. X
  727. char *getline(char *);
  728. void gl_init(int), gl_cleanup(void);
  729. X
  730. main()
  731. /* 
  732. X * just echo user input lines, letting user edit them and move through
  733. X * history list
  734. X */
  735. {
  736. X    char *p;
  737. X
  738. X    gl_init(80);
  739. X    while ((p = getline("> ")) && *p)
  740. X    fputs(p, stdout);
  741. X    gl_cleanup();
  742. }
  743. SHAR_EOF
  744. chmod 0644 testgl.c ||
  745. echo 'restore of testgl.c failed'
  746. Wc_c="`wc -c < 'testgl.c'`"
  747. test 293 -eq "$Wc_c" ||
  748.     echo 'testgl.c: original size 293, current size' "$Wc_c"
  749. fi
  750. exit 0
  751.  
  752. exit 0 # Just in case...
  753. -- 
  754. Kent Landfield                   INTERNET: kent@sparky.IMD.Sterling.COM
  755. Sterling Software, IMD           UUCP:     uunet!sparky!kent
  756. Phone:    (402) 291-8300         FAX:      (402) 291-4362
  757. Please send comp.sources.misc-related mail to kent@uunet.uu.net.
  758.